<?php

namespace Fox;

class Route {

	/**
	 * @var Route 路由
	 */
	private $urlParse;

	private $config;

	private $filters;

	public function __construct($routeConfig) {
		$this->filters = [];
		$this->config = $routeConfig;
		$this->urlParse = new UrlParse(combine_paths($this->config['full_app_path'], $this->config['controller_path']), isset($this->config['route']) ? $this->config['route'] : []);
	}

	public function addFilter($filter) {
		$this->filters[] = $filter;
	}

	public function resetRoutes($routes) {
		$this->urlParse->resetRoutes($routes);
	}

	private function includeController($dir, $ctr) {
		$ctr = ucfirst($ctr);
		$path = combine_paths($this->config['full_app_path'], $this->config['controller_path'], $dir, $ctr . 'Controller') . '.php';
		if (is_file($path)) {
			include_once $path;
			return true;
		} else {
			return false;
		}
	}

	private function realAction($controller, $req_controller_name, $req_action, $err_controller) {

		$http_method = $this->urlParse->getHttpMethod();
		if ($err_controller) {
			$realAction = is_callable([$controller, '_ec']) ? '_ec' : false;
		} else if (empty($req_controller_name)) {
			$realAction = is_callable([$controller, '_def']) ? '_def' : false;
		} else if (!empty($req_action) && is_callable([$controller, '_' . $http_method . '_' . $req_action])) {
			$realAction = '_' . $http_method . '_' . $req_action;
		} else if (is_callable([$controller, $req_action])) {
			$realAction = $req_action;
		} else if (is_callable([$controller, '_' . $http_method])) {
			$realAction = '_' . $http_method;
		} else {
			$realAction = is_callable([$controller, '_def']) ? '_def' : false;
		}
		return $realAction;
	}

	private $methods = array('_def', '_post', '_put', '_get', '_delete', '_patch', '_head');

	private function realPaths($realAction) {
		$req_paths = $this->urlParse->getPaths();
		if ($realAction == '_ec') {
			$realPaths = $req_paths;
		} else if (in_array($realAction, $this->methods)) {
			$realPaths = array_slice($req_paths, 1);
		} else {
			$realPaths = array_slice($req_paths, 2);
		}
		return $realPaths;
	}

	public function route($url = null) {

		$this->urlParse->start($url);

		//如果是闭包路由
		if ($this->urlParse->isClosure) {
			$response = $this->urlParse->callClosure();
			$this->out($response);
		} else {
			$this->dispatch();
		}
	}

	private function applyFilter($url, $method, $dir, $controller, $action, $paths) {
		foreach ($this->filters as $item) {
			$ret = $item($url, $method, $dir, $controller, $action, $paths);
			if($ret === false){
				return false;
			}else{
				$this->out($ret);
			}
		}
		return true;
	}

	private function dispatch() {
		$dfCtrl = $this->config['default_controller'];
		$req_controller_name = $this->urlParse->getController();
		$real_controller_name = $req_controller_name ?: $dfCtrl; //获取请求的controller名字
		$req_action = $this->urlParse->getAction(); //请求的action名字;
		$dir = $this->urlParse->getDir();

		if ($err_controller = !$this->includeController($dir, $real_controller_name)) {
			//尝试引入Controller文件，如果没成功引入，再引入默认controller。
			$this->includeController($dir, $dfCtrl);
			$real_controller_name = $dfCtrl;
		}

		$controller_class = ns($this->config['app_path'], $this->config['controller_path'], $dir, $real_controller_name . 'Controller');

		if (substr($req_action, 0, 1) != '_' && class_exists($controller_class, false)) {
			$controller = new $controller_class();
			$realAction = $this->realAction($controller, $req_controller_name, $req_action, $err_controller);
			if ($realAction !== false) {
				$ps = $this->realPaths($realAction);
				if($this->applyFilter($this->urlParse->url, $this->urlParse->getHttpMethod(), $dir, $real_controller_name, $realAction, $ps)){
					$this->exeAction($controller, $real_controller_name, $realAction, $ps);
				}
				return;
			}
		}
		$this->e404();
	}

	private function out($response) {
		if ($response instanceof Response) {
			$response->out();
		} else {
			echo $response;
		}
	}

	private function e404() {
		if (function_exists('e404_handler')) {
			call_user_func('e404_handler', $_SERVER[REQUEST_URI]);
		} else {
			$e404Url = '/' . ltrim($this->config['e404'], '/');
			redirect($e404Url . "?ref=$_SERVER[REQUEST_URI]", 302)->out();
		}
	}

	private function exeAction($controller, $controller_name, $action_name, $paths) {

		$ct = true;
		if (is_callable([$controller, '_before'])) {
			array_unshift($paths, $action_name);
			$ct = call_user_func_array([$controller, '_before'], $paths);
			array_shift($paths);
		}

		if ($ct !== false) {
			$response = call_user_func_array([$controller, $action_name], $paths);
			$dir = $this->urlParse->getDir();
			if (is_null($response)) {
				view(combine_paths($dir, ${controller_name} . DIRECTORY_SEPARATOR . ${action_name}))->out();
			} else {
				$this->out($response);
			}
		}

		if (is_callable([$controller, '_after'])) {
			array_unshift($paths, $action_name);
			$ct = call_user_func_array([$controller, '_after'], $paths);
			array_shift($paths);
			if ($ct === false) {
				die();
			}
		}
	}

}
